package com.itkjb.leisurely.center.config.authentication;

import com.itkjb.leisurely.authentication.core.center.config.AuthenticationCenterBeanConfig;
import com.itkjb.leisurely.authentication.core.center.support.CustomWebResponseExceptionTranslator;
import com.itkjb.leisurely.authentication.core.common.config.BaseAuthenticationConfig;
import com.itkjb.leisurely.authentication.core.common.properties.SecurityProperties;
import com.itkjb.leisurely.center.filter.CustomBasicAuthenticationFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;


/**
 * Copyright (C), 2018-2099
 * FileName: AuthorizationServerConfiguration
 *
 * @Author: lix
 * Date:     2019-05-16 17:55
 * Description: secret 登陆认抽象 配置类，具体的拦截配置，有集成类去实现
 * Version: V1.0.0
 */
@Slf4j
@Configuration
@EnableAuthorizationServer
@AutoConfigureAfter(value = {AuthenticationCenterBeanConfig.class, BaseAuthenticationConfig.class})
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired(required = false)
    DataSource dataSource;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired(required = false)
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired(required = false)
    private TokenEnhancer jwtTokenEnhancer;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    TokenStore tokenStore;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    SecurityProperties securityProperties;




    /**
     * client 的存储service  使用默认jdbc 的方式
     *
     * @param
     * @return org.springframework.security.oauth2.provider.client.JdbcClientDetailsService
     * @author xiaoyao
     * @date 2019-05-17 14:09
     * @since V1.0.0
     **/
    @Bean("jdbcClientDetailsService")
    public JdbcClientDetailsService clientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }


    @Bean
    public ApprovalStore approvalStore() {
        return new JdbcApprovalStore(dataSource);
    }

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }
    @Bean
    public WebResponseExceptionTranslator webResponseExceptionTranslator(){
        return new CustomWebResponseExceptionTranslator();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService());
    }


    /**
     * (non-javadoc)
     * 授权表达式
     *
     * @see AuthorizationServerConfigurer#configure(org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer)
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        // allowFormAuthenticationForClients
        oauthServer
                // 下面一行，加了貌似没起作用，目的是在携带token时登录时，异常交给下面配置的entryPoint处理，结果没有
                // .authenticationEntryPoint(authenticationEntryPoint) //毛线 ，资源服务器处理异常就不在这儿
                .tokenKeyAccess("permitAll()")
                //
                .checkTokenAccess("permitAll()")

                // 1、这个如果配置支持allowFormAuthenticationForClients的，
                // 且url中有client_id和client_secret的会走ClientCredentialsTokenEndpointFilter来保护
                // 2、如果没有支持allowFormAuthenticationForClients或者有支持但是url中没有client_id和client_secret的，走basic认证保护
                .allowFormAuthenticationForClients();
        //在代码中我的客户端信息每次都是放在请求头中进行发送，当我们的客户端信息不正确时服务端不会发送错误json信息而是让你重新登录，
        // 在一些app中是不能使用网页的，所以我们定义一个自己filter来处理客户端认证逻辑
        CustomBasicAuthenticationFilter customBasicAuthenticationFilter = new CustomBasicAuthenticationFilter();
        customBasicAuthenticationFilter.setClientDetailsService(clientDetailsService());
        customBasicAuthenticationFilter.setPasswordEncoder(passwordEncoder);
        customBasicAuthenticationFilter.setSecurityProperties(securityProperties);
        oauthServer.addTokenEndpointAuthenticationFilter(customBasicAuthenticationFilter);

    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore)
                .approvalStore(approvalStore())
                // 自定义的验证管理器，默认不支持密码授权，这里经过debug才发现
                .authenticationManager(authenticationManager)
                //
                .userDetailsService(userDetailsService)
                .authorizationCodeServices(authorizationCodeServices())
        ;

        //扩展token返回结果
        if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = new ArrayList();
            enhancerList.add(jwtTokenEnhancer);
            enhancerList.add(jwtAccessTokenConverter);
            tokenEnhancerChain.setTokenEnhancers(enhancerList);
            //jwt
            endpoints.tokenEnhancer(tokenEnhancerChain)
                    .accessTokenConverter(jwtAccessTokenConverter);
        }
        // 认证失败，异常处理，主要就是为了加几个属性
        endpoints.exceptionTranslator(webResponseExceptionTranslator());
    }
}
